package com.newbe.security.config;

import com.newbe.security.handler.MyAuthenticationFailureHandler;
import com.newbe.security.handler.MyAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;

import javax.sql.DataSource;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /** 具体流程：
     *1. 开启@EnableWebSecurity，让SecurityConfig extends WebSecurityConfigurerAdapter
     *2. 分别重写void configure(AuthenticationManagerBuilder auth)、void configure(HttpSecurity http)
     *3、在void configure(HttpSecurity http)方法里设置拦截资源，对资源进行认证命名
     *4、在void configure(HttpSecurity http)方法里设置登录界面放行，formLogin认证方式，并设置登陆界面路径，关闭csrf
     *5、在void configure(HttpSecurity http) 方法里设置账号，对其赋予权限
     *6、启动项目，测试：127.0.0.1:8080,分别进行管理员和用户的登录操作订单
     *7、增加权限错误和成功提示，思路：
     *7.1分别定义SuccessHandler类和FailureHandler类让他们实现AuthenticationSuccessHandler类
     *    重新其中的方法，在该方法定义成功和失败的处理流程。目前只简单的对成功和失败认证在控台打印信息
     *7.2 利用 ConfigurableServletWebServerFactory 设置状态码和设置错误路径控制器，控制错误页面
     **/
    @Autowired
    private MyAuthenticationFailureHandler FailureHandler;
    @Autowired
    private MyAuthenticationSuccessHandler SuccessHandler;

    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //通过spring security 内置的jdbc模板进行权限查询和认证
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery("select username,password,enabled from users WHERE username=?")
                .authoritiesByUsernameQuery("select username,authority from authorities where username=?")
                .passwordEncoder(new BCryptPasswordEncoder());
    }
     @Override
    protected void configure(HttpSecurity http) throws Exception {
        //1.对页面资源进行拦截和命名
        http.authorizeRequests().antMatchers("/showOrder").hasAnyAuthority("showOrder");
        http.authorizeRequests().antMatchers("/addOrder").hasAnyAuthority("addOrder");
        http.authorizeRequests().antMatchers("/deleteOrder").hasAnyAuthority("deleteOrder");
        http.authorizeRequests().antMatchers("/updateOrder").hasAnyAuthority("updateOrder");

        //2.对登录界面允许所有用户访问
        http.authorizeRequests().antMatchers("/login").permitAll();

        //3.资源需要进行登录认证，且关闭跨域允许post表单请求，loginProcessingUrl：表单路径，loginPage：登录界面
        http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin()
                .loginPage("/login").loginProcessingUrl("/loginFrom")
                //添加成功和失败处理器
                .successHandler(SuccessHandler).failureHandler(FailureHandler)
                .and().csrf().disable();
    }

    @Bean
    public UserDetailsService userDetailsService(){
        //实例化JdbcUserDetailsManager
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
        //设置数据源
        manager.setDataSource( dataSource);
        //判定数据库是否有user用户，没有则插入数据
        if(!manager.userExists("user")){
            //向数据库插入用户账号密码以及权限
            manager.createUser(User.withUsername("user").password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("USER").authorities("showOrder","addOrder").build());
        }
        //判定数据库是否有admin用户，没有则插入数据
        if(!manager.userExists("admin")){
            //向数据库插入管理员账号密码以及权限
            manager.createUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("ADMIN").authorities("deleteOrder","addOrder","showOrder","updateOrder").build());
        }
        return manager;
    }
}